﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using WebAPITest.Models;
using WebAPITest.Models.Database;
using WebAPITest.service;

namespace WebAPITest.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class RedisController : ControllerBase
    {

        private readonly WebEnterpriseIIContext _db;
        private readonly IDatabase _redis;
        private readonly IRedisService _redisService;
        private readonly IMemoryCache cache;
        public RedisController(RedisHelper client, WebEnterpriseIIContext db,IRedisService redisService)
        {
            //创建Redis连接对象
            _redis = client.GetDatabase();
            _db = db;
            _redisService = redisService;


        }
        /// <summary>
        /// 添加接口方法，测试Redis服务
        /// </summary>
        [HttpGet]
        public void SetRedisCache()
        {

            _redis.StringSet("RedisCache", "6666");
        }
        /// <summary>
        /// 利用string添加
        /// </summary>
        [HttpGet]
        public void RedisString()
        {
            var str = "广东工程职业技术学院";
            //写入String的值
            _redis.StringSet("StringCache", str);

            //获取String缓存的值
            var value = _redis.StringGet("StringCache");

            //追加字符
            _redis.StringAppend("StringCache", "666");

            //写入数据 5小时5分钟5秒 后过期
            _redis.StringSet("StringCache1", 1, new TimeSpan(5, 5, 5));

            //key为StringCache1的值自增1
            _redis.StringIncrement("StringCache1");

            //key为StringCache1的值增加10
            _redis.StringIncrement("StringCache1", 10);

            //key为StringCache1的值自减1
            _redis.StringDecrement("StringCache1");

            //key为StringCache1的值减少5
            _redis.StringDecrement("StringCache1", 5);
        }
        /// <summary>
        /// 练习
        /// </summary>
        [HttpGet]
        public void StringIphone()
        {
            //添加数据
            _redis.StringSet("goodNmae", "华为手机");
            //获取数据
            _redis.StringGet("goodName");
            //20秒后过期
            _redis.StringSet("goodName1", 1, new TimeSpan(0, 0, 20));
            //添加字符串
            _redis.StringAppend("goodName", "666");
            //查看字符串的长度
            _redis.SetLength("goodName");
        }
        /// <summary>
        /// Hash
        /// </summary>
        [HttpGet]
        public void RedisHash()
        {
            var hashKey = "hashkeyTest";
            //hash写入单个数据
            _redis.HashSet(hashKey, "name", "刘备");

            //hash写入多个数据
            var hashEntrys = new HashEntry[] { new HashEntry("num", "1406200115"), new HashEntry("class", "软件A班"), new HashEntry("age", 18) };
            _redis.HashSet(hashKey, hashEntrys);

            //hash获取数据
            var value1 = _redis.HashGet(hashKey, "name");

            //hash获取所有数据
            var value2 = _redis.HashGetAll(hashKey);

            //判断hash的属性是否存在
            var value3 = _redis.HashExists(hashKey, "name");

            //hash的属性值自增，自减
            var value4 = _redis.HashIncrement(hashKey, "age");
            value4 = _redis.HashDecrement(hashKey, "age");

            //hash删除某个属性
            _redis.HashDelete(hashKey, "class");
        }
        /// <summary>
        /// 练习二
        /// </summary>
        [HttpGet]
        public void HashTest()
        {
            //命名为liubeiinfo
            var hashKey = "liubeiinfo4";
            //添加数据
            _redis.HashSet(hashKey, "name", "刘备");
            //添加多个数据
            var hashEntrys = new HashEntry[] { new HashEntry("num", "1406200115"), new HashEntry("class", "软件技术A班"), new HashEntry("age", 1862), new HashEntry("school", "广东工程职业技术学院") };
            _redis.HashSet(hashKey, hashEntrys);
            //获取数据
            var value = _redis.HashGetAll("liubeiinfo");
            //修改num为1406200230
            _redis.HashSet("liubeiinfo", "num", "1406200230");
            //查询学号的值
            var value1 = _redis.HashGet(hashKey, "num");
        }
        /// <summary>
        /// List
        /// </summary>
        [HttpGet]
        public void RedisList()
        {
            var listkey = "testlistkey1";
            //左侧推入单个元素
            _redis.ListLeftPush(listkey, "刘备");
            //左侧推入多个元素
            var redisValues1 = new RedisValue[] { "张飞", "关羽" };
            _redis.ListLeftPush(listkey, redisValues1);
            //右侧同理
            _redis.ListRightPush(listkey, "马超");
            var redisValues2 = new RedisValue[] { "赵云", "马岱" };
            _redis.ListRightPush(listkey, redisValues2);

            //获取所有元素
            _redis.ListRange(listkey, 0, -1);

            //获取指定索引的元素
            _redis.ListGetByIndex(listkey, 2);

            //从左边取出并一个并删除元素
            _redis.ListLeftPop(listkey);
            //从右边取出并一个并删除元素
            _redis.ListRightPop(listkey);
        }
        /// <summary>
        /// List练习
        /// </summary>
        [HttpGet]
        public void RedisList1()
        {
            //添加名为userList的key
            var listkey = "userList1";
            //插入多个数据
            var redisValues1 = new RedisValue[] { "刘备", "关羽", "张飞", "赵云", "马超", "曹操", "吕布" };
            _redis.ListRightPush(listkey, redisValues1);
            //获取数据
            _redis.ListRange(listkey, 0, -1);
            //在赵云后面添加“黄盖”
            _redis.ListInsertBefore(listkey, "马超", "黄盖");
            //再次查询
            _redis.ListRange(listkey, 0, -1);//列表有多少个数据的时候就0到-1，代表查询全部
        }
        /// <summary>
        /// Set
        /// </summary>
        [HttpGet]
        public void RedisSet()
        {
            var setkey = "testsetkey1";
            //添加单个元素
            _redis.SetAdd(setkey, "刘备");
            //添加多个元素
            var redisValues1 = new RedisValue[] { "张飞", "关羽", "周瑜" };
            _redis.SetAdd(setkey, redisValues1);
            //移除某个元素
            _redis.SetRemove(setkey, "刘备");

            //获取所有成员
            var value1 = _redis.SetMembers(setkey);
            //随机获取一个元素
            var value2 = _redis.SetRandomMember(setkey);
            //随机获取多个元素
            var value3 = _redis.SetRandomMembers(setkey, 2);
            //判断是否包含某个元素
            _redis.SetContains(setkey, "马超");

            var setkey2 = "testsetkey2";
            var redisValues2 = new RedisValue[] { "张飞", "关羽", "马超", "赵云", "曹操", "黄盖" };
            _redis.SetAdd(setkey2, redisValues2);
            //获取两个集合的差集
            var value4 = _redis.SetCombine(SetOperation.Difference, setkey, setkey2);
            //获取两个集合的交集
            var value5 = _redis.SetCombine(SetOperation.Intersect, setkey, setkey2);
            //获取两个集合的并集
            var value6 = _redis.SetCombine(SetOperation.Union, setkey, setkey2);
        }
        /// <summary>
        /// Set练习
        /// </summary>
        [HttpGet]
        public void RedisSet1()
        {
            //添加刘备同学的key
            var setkey = "followList_liu1";
            //添加多个数据
            var redisValies1 = new RedisValue[] { "1214", "5651", "5654", "2668", "9595", "9955" };
            _redis.SetAdd(setkey, redisValies1);
            //添加张飞同学的key
            var setkey2 = "followList_zhang1";
            //添加多个数据
            var redisValies2 = new RedisValue[] { "5985", "5651", "5654", "6555", "9595", "9998" };
            _redis.SetAdd(setkey2, redisValies2);
            //查找两位同学共同关注的人
            var value = _redis.SetCombine(SetOperation.Union, setkey, setkey2);
        }
        /// <summary>
        /// ZSet
        /// </summary>
        [HttpGet]
        public void RedisZSet()
        {
            var zsetkey = "testzsetkey1";
            //添加单个元素
            _redis.SortedSetAdd(zsetkey, "刘备", 50);
            //添加多个元素
            var values = new[]
                { new SortedSetEntry("马超", 23), new SortedSetEntry("赵云", 50), new SortedSetEntry("关羽", 56) };
            _redis.SortedSetAdd(zsetkey, values);
            //移除某个元素
            _redis.SortedSetRemove(zsetkey, "马超");

            //给某个元素添加指定分数
            _redis.SortedSetIncrement(zsetkey, "马超", 1);
            //给某个元素减少指定分数
            _redis.SortedSetDecrement(zsetkey, "马超", 1);

            //获取某个分数段的元素
            var value1 = _redis.SortedSetRangeByScore(zsetkey, 50, 60);
            //获取某个分数段的元素及分数
            var value2 = _redis.SortedSetRangeByScoreWithScores(zsetkey, 50, 60);
            //获取某个排名段的元素
            var value3 = _redis.SortedSetRangeByRank(zsetkey, 0, 2);
            //获取某个排名段的元素及分数
            var value4 = _redis.SortedSetRangeByRankWithScores(zsetkey, 0, 2);
        }
        /// <summary>
        /// ZSet练习
        /// </summary>
        [HttpGet]
        public void RedisZSet1()
        {
            //添加key
            var zsetkey = "scorelist1";
            //添加多个数据
            var values = new[] { new SortedSetEntry("刘备", 90), new SortedSetEntry("张飞", 44), new SortedSetEntry("关羽", 55), new SortedSetEntry("赵云", 98), new SortedSetEntry("马超", 41), new SortedSetEntry("曹操", 91), new SortedSetEntry("吕布", 55), new SortedSetEntry("周瑜", 55) };
            _redis.SortedSetAdd(zsetkey, values);
            //获取2-5名的分数
            var values1 = _redis.SortedSetRangeByRank(zsetkey, 1, 4);
            //查询60份到100分的同学名单
            var value2 = _redis.SortedSetRangeByScore(zsetkey, 60, 100);
        }
        /// <summary>
        /// 拓展 添加接口方法
        /// </summary>
        [HttpGet]
        public void SerializeJSON()
        {
            //查询商品
            var good = _db.Goods.FirstOrDefault(x => x.Id == 10);
            //转化为json字符串
            var jsonStr = JsonConvert.SerializeObject(good);
            //json字符串保存到redis
            _redis.StringSet("goodinfo", jsonStr);
            //从redis取出json字符串
            var redisStr = _redis.StringGet("goodinfo");
            //转化为对象
            var jsonObj = JsonConvert.DeserializeObject<Good>(redisStr);
        }
        /// <summary>
        /// 获取商品列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public List<Good> GetGoodList()
        {
            //完成！！！ 使用本地缓存保存查询结果，缓存时间为30分钟
            //1、先查看缓存有没有商品数据 key     变量类型和变量名
            //使用redis完成
            var cache = _redis.StringGet("GoodCache");
            if (cache.HasValue)
            {
                //字符串转C
                var goodList = JsonConvert.DeserializeObject<List<Good>>(cache);
                //3、如果有则直接返回缓存数据
                return goodList;
            }
            else//没有缓存
            {
                //2、如果没有则去数据库查询并且把查询结果保存到缓存 缓存时间为30分钟
                var goods = _db.Goods.ToList();
                var jsonStr = JsonConvert.SerializeObject(goods);
                _redis.StringSet("GoodList", jsonStr, new TimeSpan(0, 30, 0));
                //cache.Set("GoodCache", goods, TimeSpan.FromMinutes(30));
                return goods;
            }

        }
        /// <summary>
        /// 获取商品详情
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        [HttpGet]
        [Authorize]//授权
        public Good GetGoodInfo(int goodId)
        {
            //商品详情
            var good = _db.Goods.FirstOrDefault(x => x.Id == goodId);
            //使用本地缓存保存登录用户浏览商品记录(多个商品)
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //之前缓存下来的浏览记录  每个用户浏览记录都是单独的 每个用户都有自己的key  UserViews28\UserViews20
            var key = $"UserViews{{userId}}";
            //取之前的浏览记录
            var cache = _redis.StringGet(key);

            //浏览商品记录
            var views = new List<Good>();
            views.Add(good);
            //是不是还应该有前面的数据
            if (cache.HasValue)
            {
                var record = JsonConvert.DeserializeObject<List<Good>>(cache);//前面看过的数据
                views.AddRange(record);
            }
            var jsonStr = JsonConvert.SerializeObject(views);
            _redis.StringSet(key, jsonStr);
            return good;
        }
        //Redis5种数据类型 是不是有更好类型处理这些场景 STRING HASH SET LIST ZSET
        //商品浏览记录数据 商品 先后顺序 LIST ZSET
        /// <summary>
        /// 使用List
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        [HttpGet]
        [Authorize]//授权
        public Good GetGoodInfo1(int goodId)
        {
            //商品详情
            var good = _db.Goods.FirstOrDefault(x => x.Id == goodId);
            //使用本地缓存保存登录用户浏览商品记录(多个商品)
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //之前缓存下来的浏览记录  每个用户浏览记录都是单独的 每个用户都有自己的key  UserViews28\UserViews20
            var key = $"UserViews{{userId}}";
            //取之前的浏览记录
            //在用户浏览商品的时候 添加商品浏览记录 商品
            //新数据在左边还是右边
            //最近浏览的商品
            //新 旧
            var jsonStr = JsonConvert.SerializeObject(good);//序列化
            _redis.ListLeftPush(key, jsonStr);
            return good;
        }
        //获取商品浏览记录 获取最新的浏览商品记录 上面是最新的
        /// <summary>
        /// 使用Zset
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        [HttpGet]
        [Authorize]//授权
        public Good GetGoodInfo2(int goodId)
        {
            //商品详情
            var good = _db.Goods.FirstOrDefault(x => x.Id == goodId);
            //使用本地缓存保存登录用户浏览商品记录(多个商品)
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //之前缓存下来的浏览记录  每个用户浏览记录都是单独的 每个用户都有自己的key  UserViews28\UserViews20
            var key = $"UserViews{{userId}}";
            //取之前的浏览记录
            //在用户浏览商品的时候 添加商品浏览记录 商品
            //新数据在左边还是右边
            //最近浏览的商品
            //新 旧
            //浏览商品的时间戳
            //var score = 0;
            var score = (DateTime.Now - new DateTime(1970, 1, 1, 8, 0, 0)).TotalSeconds;
            var jsonStr = JsonConvert.SerializeObject(good);//序列化
            _redis.SortedSetAdd(key, jsonStr, score);
            return good;
        }
        /// <summary>
        /// 购物车的添加商品
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string AddCar(int goodId)
        {
            //添加商品进入购物车
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var hashKey = "car_" + userId;
            //对购物车数量增加 如果哈希没有就添加后再加一
            _redis.HashIncrement(hashKey, goodId);
            return "添加购物车成功";
        }
        /// <summary>
        /// 减少商品
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        [HttpGet]
        public string jianCar(int goodId)
        {
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var hashKey = "car_" + userId;
            var count = _redis.HashDecrement(hashKey, goodId);

            //如果数量==0
            if (count == 0)
            {
                _redis.HashDelete(hashKey, goodId);
            }
            return "购物车减少成功";
        }
        /// <summary>
        /// 删除商品
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        [HttpGet]
        public string delectCar(int goodId)
        {
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var hashKey = "car_" + userId;
            var count = _redis.HashDelete(hashKey, goodId);
            return "购物车删除成功";
        }
        /// <summary>
        /// 查询商品
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize]//授权
        public List<GetCarModel> GetCar()
        {
            //使用本地缓存保存登录用户浏览商品记录(多个商品)
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //每个用户的Key
            var key = "car_" + userId;
            //查询hash所有数据
            var data = _redis.HashGetAll(key);
            var list = new List<GetCarModel>();
            //循环遍历hash所有数据 添加到模型
            foreach (var item in data)
            {
                //通过商品Id查询 商品详细信息
                var good = _db.Goods.FirstOrDefault(x => x.Id == (int)item.Name);
                if (good != null)
                {
                    list.Add(new GetCarModel()
                    {
                        GoodId = (int)item.Name,
                        Count = (int)item.Value,
                        Name = good.Name,
                        Price = good.Price,
                        Cover = good.Cover
                    });
                }
            }
            return list;
        }
        /// <summary>
        /// 添加搜素热词的次数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Addkey(string keyword)
        {
            var hostKey = "hostKey";
            _redis.SortedSetIncrement(hostKey, keyword, 1);
            return "添加搜索次数成功！";
        }
        /// <summary>
        /// 查询前10名热搜结果及搜索次数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public List<GetHostModel> GetHostKey()
        {
            var hostKey = "hostKey";
            //查询前十名
            var data = _redis.SortedSetRangeByRankWithScores(hostKey, 0, 9, Order.Descending);//Order.Descending为倒序
            var list = new List<GetHostModel>();
            foreach (var item in data)
            {
                list.Add(new GetHostModel()
                {
                    keyword = item.Element,
                    Count = item.Score
                });
                return list;
            }
            return list;
        }
        /// <summary>
        /// 关注用户
        /// </summary>
        /// <param name="userId"></param>
        [HttpGet]
        public string ConUser(int followUserId)
        {
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var key = "follow_"+userId;
            _redis.SetAdd(key, followUserId);
            return "关注成功";
        }
        /// <summary>
        ///取消关注
        /// </summary>
        /// <param name="followUserId"></param>
        /// <returns></returns>
        [HttpGet]
        public string UnConUser(int followUserId)
        {
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var key = "follow_" + userId;
            _redis.SetRemove(key, followUserId);
            return "取消关注成功";
        }
        /// <summary>
        /// 查看某个用户关注的人
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        [HttpGet]
        public List<int> GetConUser(int userId)
        {

            var key = "follow_" + userId;
            var data = _redis.SetMembers(key);
            return data.Select(x => (int)x).ToList();
        }
        /// <summary>
        /// 获取两个用户同时关注的人(交集)
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        [HttpGet]
        public List<int> GetConUserList(int userId1,int userId2)
        {

            var key = "follow_";
            var data = _redis.SetCombine(SetOperation.Intersect, key + userId1, key + userId2);
            return data.Select(x => (int)x).ToList();
        }
        /// <summary>
        /// 获取两个用户关注的用户总和(并集)
        /// </summary>
        /// <param name="userId1"></param>
        /// <param name="userId2"></param>
        /// <returns></returns>
        [HttpGet]
        public List<int> GetConUserList1(int userId1, int userId2)
        {

            var key = "follow_";
            var data = _redis.SetCombine(SetOperation.Union, key + userId1, key + userId2);
            return data.Select(x => (int)x).ToList();
        }
        /// <summary>
        /// 当A进入B页面,求可能认识的人：这里指的是关注B中的用户 扣去 里面也关注A的用户，就是A可能认识的人。(差集)
        /// </summary>
        /// <param name="userId1"></param>
        /// <param name="userId2"></param>
        /// <returns></returns>
        [HttpGet]
        public List<int> GetConUserList2(int userId1, int userId2)
        {

            var key = "follow_";
            var data = _redis.SetCombine(SetOperation.Difference, key + userId1, key + userId2);
            return data.Select(x => (int)x).ToList();
        }
    }
}
